# Template Method Pattern

Assembled by GimunLee

# Goal

  • Template Method Pattern의 κ°œλ…μ„ μ•Œ 수 μžˆλ‹€.
  • Template Method Pattern의 μ‚¬μš© 이점에 λŒ€ν•΄ μ„€λͺ…ν•  수 μžˆλ‹€.
  • Template Method Pattern을 μ‚¬μš©ν•  수 μžˆλ‹€.
  • ν—λ¦¬μš°λ“œ 원칙과 Template Method Pattern의 관계에 λŒ€ν•΄ μ„€λͺ…ν•  수 μžˆλ‹€.

# What is Template Method Pattern?

Template Method Pattern μ—μ„œλŠ” λ©”μ†Œλ“œμ—μ„œ μ•Œκ³ λ¦¬μ¦˜μ˜ 골격을 μ •μ˜ν•©λ‹ˆλ‹€. μ•Œκ³ λ¦¬μ¦˜μ˜ μ—¬λŸ¬ 단계 쀑 μΌλΆ€λŠ” μ„œλΈŒν΄λž˜μŠ€μ—μ„œ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν…œν”Œλ¦Ώ λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•˜λ©΄ μ•Œκ³ λ¦¬μ¦˜μ˜ κ΅¬μ‘°λŠ” κ·ΈλŒ€λ‘œ μœ μ§€ν•˜λ©΄μ„œ μ„œλΈŒν΄λž˜μŠ€μ—μ„œ νŠΉμ • 단계λ₯Ό μž¬μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이번 μž₯μ—μ„œλŠ” 주어진 상황을 톡해 Template Method Pattern의 μ‚¬μš© 이점과 κ΅¬ν˜„ 방법에 λŒ€ν•΄ μ•Œμ•„λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.


# μŠ€νƒ€λ²„μ¦ˆ 컀피 λ°”λ¦¬μŠ€νƒ€ ν›ˆλ ¨μš© 메뉴얼

μŠ€νƒ€λ²„μ¦ˆ 컀피 λ§Œλ“œλŠ” 법

  1. 물을 끓인닀.
  2. λ“λŠ” 물에 컀피λ₯Ό μš°λ €λ‚Έλ‹€.
  3. 컀피λ₯Ό 컡에 λ”°λ₯Έλ‹€.
  4. 섀탕과 우유λ₯Ό μΆ”κ°€ν•œλ‹€.

μŠ€νƒ€λ²„μ¦ˆ 홍차 λ§Œλ“œλŠ” 법

  1. 물을 끓인닀.
  2. λ“λŠ” 물에 μ°¨λ₯Ό μš°λ €λ‚Έλ‹€.
  3. μ°¨λ₯Ό 컡에 λ”°λ₯Έλ‹€.
  4. 레λͺ¬μ„ μΆ”κ°€ν•œλ‹€.

μœ„μ™€ 같은 메뉴얼을 μ½”λ”©μœΌλ‘œ κ΅¬ν˜„ν•œλ‹€λ©΄ μ—¬λŸ¬λΆ„μ€ μ–΄λ–»κ²Œ κ΅¬ν˜„ν•˜μ‹€κ±΄κ°€μš”?


# 일반적인 컀피 및 홍차 클래슀 λ§Œλ“€κΈ° (JAVA)

# Coffee Class

public class Coffee {
   	void prepareRecipe(){
        boilWater(); // 물을 끓인닀.
        brewCoffeeGrinds(); // λ“λŠ” 물에 컀피λ₯Ό μš°λ €λ‚Έλ‹€.
        pourInCup(); // 컀피λ₯Ό 컡에 λ”°λ₯Έλ‹€.
        addSugarAndMilk(); // 섀탕과 우유λ₯Ό μΆ”κ°€ν•œλ‹€.
    }
    
    public void boilWater(){
        System.out.println("λ¬Ό λ“μ΄λŠ” 쀑");
    }
    
    public void brewCoffeeGrinds(){
        System.out.println("ν•„ν„°λ₯Ό ν†΅ν•΄μ„œ 컀피λ₯Ό μš°λ €λ‚΄λŠ” 쀑");
    }
    
    public void pourInCup(){
        System.out.println("컡에 λ”°λ₯΄λŠ” 쀑");
    }
    
    public void addSugarAndMilk(){
        System.out.println("섀탕과 우유λ₯Ό μΆ”κ°€ν•˜λŠ” 쀑");
    }
}

# Tea Class

public class Tea {
   	void prepareRecipe(){
        boilWater(); // 물을 끓인닀.
        steepTeaBag(); // λ“λŠ” 물에 μ°¨λ₯Ό μš°λ €λ‚Έλ‹€.
        pourInCup(); // μ°¨λ₯Ό 컡에 λ”°λ₯Έλ‹€.
        addLemon(); // 레λͺ¬μ„ μΆ”κ°€ν•œλ‹€.
    }
    
    public void boilWater(){
        System.out.println("λ¬Ό λ“μ΄λŠ” 쀑");
    }
    
    public void steepTeaBag(){
        System.out.println("μ°¨λ₯Ό μš°λ €λ‚΄λŠ” 쀑");
    }
    
    public void pourInCup(){
        System.out.println("컡에 λ”°λ₯΄λŠ” 쀑");
    }
    
    public void addLemon(){
        System.out.println("레λͺ¬μ„ μΆ”κ°€ν•˜λŠ” 쀑");
    }
}

μ½”λ“œκ°€ μ€‘λ³΅λ˜μ–΄ 있으면 λ””μžμΈμ„ 고쳐야 ν•˜μ§€ μ•Šμ„κΉŒ 생각해 λ³΄λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. Coffeeν•˜κ³  Tea ν΄λž˜μŠ€κ°€ 거의 λ˜‘κ°™μœΌλ‹ˆκΉŒ 곡톡적인 뢀뢄을 μΆ”μƒν™”μ‹œμΌœμ„œ 베이슀 클래슀λ₯Ό λ§Œλ“œλŠ” 것이 쒋지 μ•Šμ„κΉŒμš”?


# 일반적인 컀피 및 홍차 좔상화

μ—¬λŸ¬λΆ„λ„ μœ„μ™€ 같이 μƒκ°ν•˜μ…¨λ‚˜μš”? 이 밖에 Coffee와 Tea 사이에 곡톡점이 더 μ—†λŠ”μ§€ 생각해 λ΄…μ‹œλ‹€.


# Template Method Pattern μ‚¬μš©ν•˜κΈ°

컀피와 홍차 사이에 λ˜λ‹€λ₯Έ 곡톡점은 λ§Œλ“œλŠ” λ²•μ˜ μ•Œκ³ λ¦¬μ¦˜ 이 λ˜‘κ°™λ‹€λŠ” 것 μž…λ‹ˆλ‹€. λ‹€μ‹œ ν•œλ²ˆ μŠ€νƒ€λ²„μ¦ˆμ˜ 메뉴얼을 μ‚΄νŽ΄λ³΄λ©΄,

  1. 물을 끓인닀.
  2. 뜨거운 물을 μ΄μš©ν•˜μ—¬ 컀피 λ˜λŠ” 홍차 λ₯Ό μš°λ €λ‚Έλ‹€.
  3. λ§Œλ“€μ–΄μ§„ 음료λ₯Ό 컡에 λ”°λ₯Έλ‹€.
  4. 각 μŒλ£Œμ— λ§žλŠ” 첨가물을 μΆ”κ°€ν•œλ‹€.

μ΄λ ‡κ²Œ λ³΄λ‹ˆ, 이미 μΆ”μƒν™”ν•œ 1번, 3번 이외에도 λ‚˜λ¨Έμ§€ 2번, 4번 λ˜ν•œ 좔상화가 κ°€λŠ₯ν•  것 κ°™μŠ΅λ‹ˆλ‹€. λ¨Όμ €, 컀피와 ν™μ°¨μ—μ„œ μ‚¬μš©ν•΄λ„ μ΄μƒν•˜μ§€ μ•Šλ„λ‘ 수퍼클래슀의 λ©”μ†Œλ“œ 이름을 λ°”κΏ”λ³΄κ² μŠ΅λ‹ˆλ‹€.

# CaffeineBeverage Class

public abstract class CaffeineBeverage {
	
    final void prepareRecipe() { // μ„œλΈŒν΄λž˜μŠ€μ—μ„œ 이 λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν•΄μ„œ μ•„λ¬΄λ ‡κ²Œλ‚˜ 
                                 // 음료λ₯Ό λ§Œλ“€μ§€ λͺ»ν•˜λ„둝 final둜 μ„ μ–Έν•΄μ€λ‹ˆλ‹€.
        boilWater();
        brew(); 
        pourInCup();
        addCondiments();
    }
    
    void boilWater() {
        System.out.println("물을 λ“μ΄λŠ” 쀑");
    }
    
    abstract void brew();           // Coffee와 Teaμ—μ„œ 이 λ©”μ†Œλ“œλ₯Ό μ„œλ‘œ λ‹€λ₯Έ λ°©μ‹μœΌλ‘œ 
    abstract void addCondiments();  // μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— 좔상 λ©”μ†Œλ“œλ‘œ μ„ μ–Έν•΄μ€λ‹ˆλ‹€.
    
    void pourInCup() {
        System.out.println("컡에 λ”°λ₯΄λŠ” 쀑");
    }
    
}

# Tea Class

public class Tea extends CaffeineBeverage {
	public void brew() {
		System.out.println("μ°¨λ₯Ό μš°λ €λ‚΄λŠ” 쀑");
	}
	
	public void addCondiments() {
		System.out.println("레λͺ¬μ„ μΆ”κ°€ν•˜λŠ” 쀑");
	}
}

# Coffee Class

public class Coffee extends CaffeineBeverage {
	public void brew() {
		System.out.println("ν•„ν„°λ‘œ 컀피λ₯Ό μš°λ €λ‚΄λŠ” 쀑");
	}
	
	public void addCondiments() {
		System.out.println("섀탕과 컀피λ₯Ό μΆ”κ°€ν•˜λŠ” 쀑");
	}
}

이처럼 Template Method Pattern μ—μ„œλŠ” μ•Œκ³ λ¦¬μ¦˜μ˜ 각 단계듀을 μ •μ˜ν•˜λ©°, κ·Έ 쀑 ν•œ 개 μ΄μƒμ˜ 단계가 μ„œλΈŒν΄λž˜μŠ€μ— μ˜ν•΄ 제곡될 수 μžˆμŠ΅λ‹ˆλ‹€.


# Template Method Pattern의 μ‚¬μš© 이점

  • CoffeineBeverage ν΄λž˜μŠ€μ—μ„œ μž‘μ—…μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. μ•Œκ³ λ¦¬μ¦˜μ„ 혼자 λ…μ ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • CoffeineBeverage 덕뢄에 μ„œλΈŒν΄λž˜μŠ€μ—μ„œ μ½”λ“œλ₯Ό μž¬μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ•Œκ³ λ¦¬μ¦˜μ€ ν•œ ꡰ데에 λͺ¨μ—¬ 있기 λ•Œλ¬Έμ— κ·Έ λΆ€λΆ„λ§Œ 고치면 λ©λ‹ˆλ‹€.
  • Template Method Pattern을 μ‚¬μš©ν•˜λŠ” λ²„μ „μ—μ„œλŠ” λ‹€λ₯Έ 카페인 μŒλ£Œλ„ μ‰½κ²Œ μΆ”κ°€ν•  수 μžˆλŠ” ν”„λ ˆμž„μ›Œν¬λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. 카페인 음료λ₯Ό μΆ”κ°€ν•  λ•Œ λͺ‡ 가지 λ©”μ†Œλ“œλ§Œ μΆ”κ°€ν•˜λ©΄ λ©λ‹ˆλ‹€.
  • CaffeineBeverage ν΄λž˜μŠ€μ— μ•Œκ³ λ¦¬μ¦˜μ— λŒ€ν•œ 지식이 μ§‘μ€‘λ˜μ–΄ 있으면 일뢀 κ΅¬ν˜„λ§Œ μ„œλΈŒν΄λž˜μŠ€μ— μ˜μ‘΄ν•©λ‹ˆλ‹€.

# Template Method Patternκ³Ό Hook

후크(hook)λŠ” 좔상 ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλ˜λŠ” λ©”μ†Œλ“œκΈ΄ ν•˜μ§€λ§Œ 기본적인 λ‚΄μš©λ§Œ κ΅¬ν˜„λ˜μ–΄ μžˆκ±°λ‚˜ 아무 μ½”λ“œλ„ λ“€μ–΄μžˆμ§€ μ•Šμ€ λ©”μ†Œλ“œμž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ„œλΈŒν΄λž˜μŠ€ μž…μž₯μ—μ„œλŠ” λ‹€μ–‘ν•œ μœ„μΉ˜μ—μ„œ μ•Œκ³ λ¦¬μ¦˜μ— 끼어듀 수 μžˆμŠ΅λ‹ˆλ‹€.

ν›„ν¬λŠ” λ‹€μ–‘ν•œ μš©λ„λ‘œ μ‚¬μš©ν•  수 μžˆλŠ”λ° μš°μ„  ν•œκ°€μ§€ μ‚¬μš©λ²•μ„ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

# CaffeineBeverage Class

public abstract class CaffeineBeverageWithHook {
    void prepareRecipe() {
        boilWater();
        brew(); 
        pourInCup();
        
        if(customerWantsCondiments()){
        	addCondiments();    
        }
    }
    
    void boilWater() {
        System.out.println("물을 λ“μ΄λŠ” 쀑");
    }
    
    abstract void brew();          
    abstract void addCondiments(); 
    
    void pourInCup() {
        System.out.println("컡에 λ”°λ₯΄λŠ” 쀑");
    }
    
    boolean customerWantsCondiments() { // 이 λ©”μ†Œλ“œλŠ” μ„œλΈŒν΄λž˜μŠ€μ—μ„œ ν•„μš”μ— 따라 
        return true;                    // μ˜€λ²„λΌμ΄λ“œ ν•  수 μžˆλŠ” λ©”μ†Œλ“œμ΄λ―€λ‘œ ν›„ν¬μž…λ‹ˆλ‹€.
    }
}

이 후크λ₯Ό μ‚¬μš©ν•˜λ €λ©΄, Coffeeλ‚˜ Tea와 같은 μ„œλΈŒν΄λž˜μŠ€μ—μ„œ ν•„μš”μ— 따라 customerWantsConidments() λ₯Ό μ˜€λ²„λΌμ΄λ“œν•΄μ„œ μŒλ£Œμ— 첨가물을 좔가할지 μ—¬λΆ€λ₯Ό κ²°μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


# ν—λ¦¬μš°λ“œ 원칙

ν—λ¦¬μš°λ“œ 원칙은 λ””μžμΈ μ›μΉ™μœΌλ‘œ λ¨Όμ € μ—°λ½ν•˜μ§€ λ§ˆμ„Έμš”. 저희가 μ—°λ½λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€. μž…λ‹ˆλ‹€.

ν—λ¦¬μš°λ“œ 원칙을 ν™œμš©ν•˜λ©΄ μ˜μ‘΄μ„± λΆ€νŒ¨(dependency rot) λ₯Ό 방지할 수 μžˆμŠ΅λ‹ˆλ‹€. μ˜μ‘΄μ„± λΆ€νŒ¨λŠ” κ΅¬μ„±μš”μ†Œ κ°„μ˜ μ˜μ‘΄μ„±μ΄ λ³΅μž‘ν•˜κ²Œ κΌ¬μ—¬μžˆλŠ” 것을 μ˜ν•˜λŠ”λ°, μ΄λ ‡κ²Œ μ˜μ‘΄μ„±μ΄ λΆ€νŒ¨ν•˜λ©΄ μ‹œμŠ€ν…œμ΄ μ–΄λ–€ μ‹μœΌλ‘œ λ””μžμΈλœ 것인지 거의 아무도 μ•Œμ•„λ³Ό 수 μ—†κ²Œ λ©λ‹ˆλ‹€.

ν—λ¦¬μš°λ“œ 원칙을 μ‚¬μš©ν•˜λ©΄, μ €μˆ˜μ€€ κ΅¬μ„±μš”μ†Œμ—μ„œ μ‹œμŠ€ν…œμ— 접속을 ν•  μˆ˜λŠ” μžˆμ§€λ§Œ, μ–Έμ œ μ–΄λ–€ μ‹μœΌλ‘œ κ·Έ κ΅¬μ„±μš”μ†Œλ“€μ„ μ‚¬μš©ν• μ§€λŠ” κ³ μˆ˜μ€€ κ΅¬μ„±μš”μ†Œμ—μ„œ κ²°μ •ν•˜κ²Œ λ©λ‹ˆλ‹€. 즉, κ³ μˆ˜μ€€ κ΅¬μ„±μš”μ†Œμ—μ„œ μ €μˆ˜μ€€ κ΅¬μ„±μš”μ†Œμ—κ²Œ "λ¨Όμ € μ—°λ½ν•˜μ§€λ§ˆμ„Έμš”. μ œκ°€ λ¨Όμ € μ—°λ½λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€." 라고 μ–˜κΈ°λ₯Ό ν•˜λŠ” 것과 κ°™μŠ΅λ‹ˆλ‹€.


# ν—λ¦¬μš°λ“œ 원칙과 Template Method Pattern

μœ„μ˜ μ˜ˆμ œμ—μ„œ μ„€λͺ…ν•˜μžλ©΄, CaffeineBeverageλŠ” κ³ μˆ˜μ€€ κ΅¬μ„±μš”μ†Œμž…λ‹ˆλ‹€. 음료λ₯Ό λ§Œλ“œλŠ” 방법에 ν•΄λ‹Ήν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ„ μž₯μ•…ν•˜κ³  있고, λ©”μ†Œλ“œ κ΅¬ν˜„μ΄ ν•„μš”ν•œ μƒν™©μ—μ„œλ§Œ μ„œλΈŒν΄λž˜μŠ€λ₯Ό λΆˆλŸ¬λƒ…λ‹ˆλ‹€.

μ„œλΈŒν΄λž˜μŠ€λŠ” μžμ§ˆκ΅¬λ ˆν•œ λ©”μ†Œλ“œ κ΅¬ν˜„μ„ μ œκ³΅ν•˜κΈ° μœ„ν•œ μš©λ„λ‘œλ§Œ μ‚¬μš©λ©λ‹ˆλ‹€.

CaffaeineBeverage 클래슀의 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλŠ” Teaλ‚˜ Coffee 같은 ꡬ상 ν΄λž˜μŠ€κ°€ μ•„λ‹Œ CaffeineBeverage에 μΆ”μƒν™”λ˜μ–΄ μžˆλŠ” 뢀뢄에 μ˜μ‘΄ν•˜κ²Œ λ©λ‹ˆλ‹€. κ·Έλ ‡κ²Œ ν•¨μœΌλ‘œμ¨ 전체 μ‹œμŠ€ν…œμ˜ μ˜μ‘΄μ„±μ΄ 쀄어듀 수 μžˆμŠ΅λ‹ˆλ‹€.

이 밖에도 ν—λ¦¬μš°λ“œ 원칙을 μ‚¬μš©ν•˜λŠ” λ””μžμΈ νŒ¨ν„΄μ—λŠ” Factory Method Pattern , Observer Pattern 등이 μžˆμŠ΅λ‹ˆλ‹€.


# Conclusion

Template Method Pattern은 ν”„λ ˆμž„μ›Œν¬λ₯Ό λ§Œλ“œλŠ” 데 μ•„μ£Ό ν›Œλ₯­ν•œ λ””μžμΈ 도ꡬ라고 ν•©λ‹ˆλ‹€. ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ μž‘μ—…μ΄ μ²˜λ¦¬λ˜λŠ” 방식은 μ œμ–΄ν•  수 μžˆμœΌλ©΄μ„œλ„, ν”„λ ˆμž„μš°ν¬μ—μ„œ μ²˜λ¦¬ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ˜ 각 λ‹¨κ³„λŠ” κ·Έ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•˜λŠ” μ‚¬λžŒ λ§ˆμŒλŒ€λ‘œ 지정할 수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

Template Method Pattern와 Strategy Pattern은 μƒλ‹Ήνžˆ μœ μ‚¬ν•΄λ³΄μ΄λŠ”λ°, λΆ„λͺ…ν•œ 차이가 μžˆμŠ΅λ‹ˆλ‹€. 이 뢀뢄에 λŒ€ν•΄μ„œλŠ” μΆ”ν›„ μ •λ¦¬ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.


# References

  • Head First Design Patterns
Last Updated: 8/12/2020, 1:33:42 PM